﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright company="HillHouse" file="MilHats.cs">
//   Copyright © 2009-2011 HillHouse
// </copyright>
// <summary>
//   Supports decorations on top of the standard military symbol. 
// </summary>
// <license>
//   Licensed under the Ms-PL license.
// </license>
// <homepage>
//   http://milsym.codeplex.com
// </homepage>
// --------------------------------------------------------------------------------------------------------------------

namespace MilSym.MilSymbol
{
    using System.Windows;
    using System.Windows.Media;
    using System.Windows.Shapes;
    using Schemas;

    /// <summary>
    /// Supports decorations on top of the standard military symbol.
    /// </summary>
    internal static class MilHats
    {
        /// <summary>
        /// Generates the correct combination of task force, installation, and feint dummy for a symbol.
        /// </summary>
        /// <param name="ms">
        /// The symbol to which the generated rendering is attached.
        /// </param>
        /// <param name="symbolCode">
        /// The symbol code for the given symbol
        /// </param>
        /// <returns>
        /// The maximum height of the generated rendering.
        /// </returns>
        internal static double Generate(MilSymbol ms, string symbolCode)
        {
            // This is the maximum height generated by this combination of pieces
            Rect r = ms.BaseRect;
            if (r.IsEmpty)
            {
                return 0;
            }

            double height = r.Top;

            if (!SymbolData.Check(ref symbolCode))
            {
                return height;
            }

            char code = ModifierCode.GetCode(symbolCode);

            switch (code)
            {
                case ModifierCode.Headquarters: // headquarters
                    ms.AddChild("HQ", GenerateHeadquarters(ms));
                    break;
                case ModifierCode.TaskForceHeadquarters: // task force, headquarters
                    ms.AddChild("TF", GenerateTaskForce(ms, out height));
                    ms.AddChild("HQ", GenerateHeadquarters(ms));
                    break;
                case ModifierCode.FeintDummyHeadquarters: // feint dummy, headquarters
                    ms.AddChild("FD", GenerateFeintDummy(ms, ref height));
                    ms.AddChild("HQ", GenerateHeadquarters(ms));
                    break;
                case ModifierCode.FeintDummyTaskForceHeadquarters: // feint dummy, task force, headquarters
                    ms.AddChild("TF", GenerateTaskForce(ms, out height));
                    ms.AddChild("FD", GenerateFeintDummy(ms, ref height));
                    ms.AddChild("HQ", GenerateHeadquarters(ms));
                    break;
                case ModifierCode.TaskForce: // task force
                    ms.AddChild("TF", GenerateTaskForce(ms, out height));
                    break;
                case ModifierCode.FeintDummy: // feint dummy
                    ms.AddChild("FD", GenerateFeintDummy(ms, ref height));
                    break;
                case ModifierCode.FeintDummyTaskForce: // feint dummy/task force
                    ms.AddChild("TF", GenerateTaskForce(ms, out height));
                    ms.AddChild("FD", GenerateFeintDummy(ms, ref height));
                    break;
                case ModifierCode.Installation: // installation
                    ms.AddChild("Installation", GenerateInstallation(ms, out height));

                    // There is an unfortunate overloading of the echelon character in the standard
                    if (Echelon.GetCode(symbolCode) == 'B')
                    {
                        ms.AddChild("FD", GenerateFeintDummy(ms, ref height));
                    }

                    break;
                case ModifierCode.Mobility: // mobility
                case ModifierCode.Towed: // towed
                    break;
            }

            return height;
        }

        /// <summary>
        /// Generate the task force rendering for the passed in symbol.
        /// </summary>
        /// <param name="ms">
        /// The military symbol for which to provide the task force rendering.
        /// </param>
        /// <param name="height">
        /// The height at which to provide the task force rendering.
        /// </param>
        /// <returns>
        /// The graphics Path object that represents the task force rendering.
        /// </returns>
        private static Path GenerateTaskForce(MilSymbol ms, out double height)
        {
            height = -185;
            double bottom = 0;
            const double Wide = 54;
            int si = StandardIdentity.GetNormalizedStandardIdentity(ms.SymbolCode);

            if (SymbolData.IsTopFlat(ms.SymbolCode))
            {
                bottom = ms.BaseRect.Top;
                if (si == StandardIdentity.Unknown)
                {
                    bottom += SymbolData.HalfWidth;
                }

                height = bottom - 75;
            }
            else
            {
                int bd = CategoryBattleDimension.GetCode(ms.SymbolCode);
                bool ap = bd == CategoryBattleDimension.BdSpace || bd == CategoryBattleDimension.BdAir;

                // The cases cover those surfaces which are flat on top versus those that are not
                switch (si)
                {
                    case StandardIdentity.Unknown:
                        if (ap) 
                        {
                            height = -230;  // space
                            bottom = -133;
                        }
                        else 
                        {
                            height = -250;  // ground
                            bottom = -154;
                        }

                        break;
                    case StandardIdentity.Hostile:
                        if (ap)
                        {
                            height = -230;  // space
                            bottom = -107;
                        }
                        else 
                        {
                            height = -253;  // ground
                            bottom = -119;
                        }

                        break;
                    case StandardIdentity.Friend:
                        height = -222;
                        if (ap)
                        {
                            bottom = -121;  // space
                        }
                        else 
                        {
                            bottom = -137;  // ground circles
                        }

                        break;
                }
            }

            return new Path
            {
                Style = SymbolData.GetStyle("BS10"),
                Data = new PathGeometry
                {
                    Figures = new PathFigureCollection
                    {
                        new PathFigure
                        {
                            StartPoint = new Point(Wide, bottom),
                            Segments = new PathSegmentCollection
                            {
                                new LineSegment { Point = new Point(Wide, height) },
                                new LineSegment { Point = new Point(-Wide, height) },
                                new LineSegment { Point = new Point(-Wide, bottom) }
                            }
                        }
                    }
                }
            };
        }

        /// <summary>
        /// Generate the feint dummy rendering for the passed in symbol.
        /// </summary>
        /// <param name="ms">
        /// The military symbol for which to provide the feint dummy rendering.
        /// </param>
        /// <param name="height">
        /// The height at which to provide the feint dummy rendering.
        /// </param>
        /// <returns>
        /// The graphics Path object that represents the feint dummy rendering.
        /// </returns>
        private static Path GenerateFeintDummy(MilSymbol ms, ref double height)
        {
            if (!string.IsNullOrEmpty(Echelon.GetEchelonSymbol(ms.SymbolCode)))
            {
                height = ms.Bounds.Top;
            }

            if (SymbolData.IsTopFlat(ms.SymbolCode))
            {
                double minimum = ms.BaseRect.Top;
                if (height >= minimum)
                {
                    height = minimum - 28;
                }
            }

            double bottom = height + 23;
            height = height - 27;
            const double Wide = 126;

            var st = new Style(typeof(Shape)) { BasedOn = SymbolData.GetStyle("BS10") };
            st.SetDashArray(3, 1);

            return new Path
            {
                Style = st,
                Data = new PathGeometry
                {
                    Figures = new PathFigureCollection
                    {
                        new PathFigure
                        {
                            StartPoint = new Point(-Wide, bottom),
                            Segments = new PathSegmentCollection
                            {
                                new LineSegment { Point = new Point(0.0, height) },
                                new LineSegment { Point = new Point(Wide, bottom) }
                            }
                        }
                    }
                }
            };
        }

        /// <summary>
        /// Generate the headquarters rendering for the passed in symbol.
        /// </summary>
        /// <param name="ms">
        /// The military symbol for which to provide the headquarters rendering.
        /// </param>
        /// <returns>
        /// The graphics Shape object that represents the headquarters rendering.
        /// </returns>
        private static Shape GenerateHeadquarters(MilSymbol ms)
        {
            Rect b = ms.BaseRect;
            double headquartersFactor = SymbolData.GetHqFactor(ms.SymbolCode);

            return new Path
            {
                Style = SymbolData.GetStyle("BS10"),
                Data = new PathGeometry
                {
                    Figures = new PathFigureCollection
                    {
                        new PathFigure
                        {
                            // The +/- HalfWidth accounts for the Bounds being on the outside of the symbol
                            StartPoint = new Point(b.Left + SymbolData.HalfWidth, b.Top + (headquartersFactor * b.Height)),
                            Segments = new PathSegmentCollection
                            {
                                new LineSegment { Point = new Point(b.Left + SymbolData.HalfWidth, b.Bottom + (0.3 * b.Width)) }
                            }
                        }
                    }
                }
            };
        }

        /// <summary>
        /// Generate the unknown space installation rendering for the passed in symbol.
        /// </summary>
        /// <param name="br">
        /// The brush to use for the unknown space installation rendering.
        /// </param>
        /// <param name="height">
        /// The height at which to provide the unknown space installation rendering.
        /// </param>
        /// <returns>
        /// The graphics Path object that represents the unknown space installation rendering.
        /// </returns>
        private static Path UnknownSpaceInstallation(Brush br, double height)
        {
            return new Path
            {
                Fill = br,
                Data = new PathGeometry
                {
                    Figures = new PathFigureCollection
                    {
                        new PathFigure
                        {
                            StartPoint = new Point(-61, -133),
                            Segments = new PathSegmentCollection
                            {
                                new BezierSegment
                                {
                                    Point1 = new Point(-23, -168),
                                    Point2 = new Point(23, -168),
                                    Point3 = new Point(61, -133)
                                },
                                new LineSegment { Point = new Point(61, height) },
                                new LineSegment { Point = new Point(-61, height) }
                            }
                        }
                    }
                }
            };
        }

        /// <summary>
        /// Generate the unknown ground installation rendering for the passed in symbol.
        /// </summary>
        /// <param name="br">
        /// The brush to use for the unknown ground installation rendering.
        /// </param>
        /// <param name="height">
        /// The height at which to provide the unknown ground installation rendering.
        /// </param>
        /// <returns>
        /// The graphics Path object that represents the unknown ground installation rendering.
        /// </returns>
        private static Path UnknownGroundInstallation(Brush br, double height)
        {
            return new Path
            {
                Fill = br,
                Data = new PathGeometry
                {
                    Figures = new PathFigureCollection
                    {
                        new PathFigure
                        {
                            StartPoint = new Point(-61, -154),
                            Segments = new PathSegmentCollection
                            {
                                new BezierSegment
                                {
                                    Point1 = new Point(-24, -183),
                                    Point2 = new Point(24, -183),
                                    Point3 = new Point(61, -154)
                                },
                                new LineSegment { Point = new Point(61, height) },
                                new LineSegment { Point = new Point(-61, height) }
                            }
                        }
                    }
                }
            };
        }

        /// <summary>
        /// Generate the hostile space installation rendering for the passed in symbol.
        /// </summary>
        /// <param name="br">
        /// The brush to use for the hostile space installation rendering.
        /// </param>
        /// <param name="height">
        /// The height at which to provide the hostile space installation rendering.
        /// </param>
        /// <returns>
        /// The graphics Shape object that represents the hostile space installation rendering.
        /// </returns>
        private static Shape HostileSpaceInstallation(Brush br, double height)
        {
            return new Path
            {
                Fill = br,
                Data = new PathGeometry
                {
                    Figures = new PathFigureCollection
                    {
                        new PathFigure
                        {
                            StartPoint = new Point(-61, -107),
                            Segments = new PathSegmentCollection
                            {
                                new LineSegment { Point = new Point(0, -163) },
                                new LineSegment { Point = new Point(61, -107) },
                                new LineSegment { Point = new Point(61, height) },
                                new LineSegment { Point = new Point(-61, height) }
                            }
                        }
                    }
                }
            };
        }

        /// <summary>
        /// Generate the hostile ground installation rendering for the passed in symbol.
        /// </summary>
        /// <param name="br">
        /// The brush to use for the hostile ground installation rendering.
        /// </param>
        /// <param name="height">
        /// The height at which to provide the hostile ground installation rendering.
        /// </param>
        /// <returns>
        /// The graphics Shape object that represents the hostile ground installation rendering.
        /// </returns>
        private static Shape HostileGroundInstallation(Brush br, double height)
        {
            return new Path
            {
                Fill = br,
                Data = new PathGeometry
                {
                    Figures = new PathFigureCollection
                    {
                        new PathFigure
                        {
                            StartPoint = new Point(-61, -119),
                            Segments = new PathSegmentCollection
                            {
                                new LineSegment { Point = new Point(0, -180) },
                                new LineSegment { Point = new Point(61, -119) },
                                new LineSegment { Point = new Point(61, height) },
                                new LineSegment { Point = new Point(-61, height) }
                            }
                        }
                    }
                }
            };
        }

        /// <summary>
        /// Generate the friendly space installation rendering for the passed in symbol.
        /// </summary>
        /// <param name="br">
        /// The brush to use for the friendly space installation rendering.
        /// </param>
        /// <param name="height">
        /// The height at which to provide the friendly space installation rendering.
        /// </param>
        /// <returns>
        /// The graphics Path object that represents the friendly space installation rendering.
        /// </returns>
        private static Path FriendSpaceInstallation(Brush br, double height)
        {
            return new Path
            {
                Fill = br,
                Data = new PathGeometry
                {
                    Figures = new PathFigureCollection
                    {
                        new PathFigure
                        {
                            StartPoint = new Point(-61, -121),
                            Segments = new PathSegmentCollection
                            {
                                new BezierSegment
                                {
                                    Point1 = new Point(-23, -157),
                                    Point2 = new Point(23, -157),
                                    Point3 = new Point(61, -121)
                                },
                                new LineSegment { Point = new Point(61, height) },
                                new LineSegment { Point = new Point(-61, height) }
                            }
                        }
                    }
                }
            };
        }

        /// <summary>
        /// Generate the friendly circular installation rendering for the passed in symbol.
        /// For example, some sea surface symbols are represented by circles.
        /// </summary>
        /// <param name="br">
        /// The brush to use for the friendly circular installation rendering.
        /// </param>
        /// <param name="height">
        /// The height at which to provide the friendly circular installation rendering.
        /// </param>
        /// <returns>
        /// The graphics Path object that represents the friendly circular installation rendering.
        /// </returns>
        private static Path FriendCircleInstallation(Brush br, double height)
        {
            return new Path
            {
                Fill = br,
                Data = new PathGeometry
                {
                    Figures = new PathFigureCollection
                    {
                        new PathFigure
                        {
                            StartPoint = new Point(-61, -137),
                            Segments = new PathSegmentCollection
                            {
                                new BezierSegment
                                {
                                    Point1 = new Point(-23, -154),
                                    Point2 = new Point(23, -154),
                                    Point3 = new Point(61, -137)
                                },
                                new LineSegment { Point = new Point(61, height) },
                                new LineSegment { Point = new Point(-61, height) }
                            }
                        }
                    }
                }
            };
        }

        /// <summary>
        /// Generate the generic installation rendering for the passed in symbol.
        /// This is typically a black rectangular block placed on top of the symbol.
        /// </summary>
        /// <param name="br">
        /// The brush to use for the generic installation rendering.
        /// </param>
        /// <param name="height">
        /// The height at which to provide the generic installation rendering.
        /// </param>
        /// <param name="bottom">
        /// The base at which to provide the generic installation rendering.
        /// This is typically the top of the symbol.
        /// </param>
        /// <returns>
        /// The graphics Shape object that represents the generic installation rendering.
        /// </returns>
        private static Shape GenericInstallation(Brush br, double height, double bottom)
        {
            return new Path
            {
                Fill = br,
                Data = new PathGeometry
                {
                    Figures = new PathFigureCollection
                    {
                        new PathFigure
                        {
                            StartPoint = new Point(-61, bottom),
                            Segments = new PathSegmentCollection
                            {
                                new LineSegment { Point = new Point(61, bottom) },
                                new LineSegment { Point = new Point(61, height) },
                                new LineSegment { Point = new Point(-61, height) }
                            }
                        }
                    }
                }
            };
        }

        /// <summary>
        /// Generate the installation rendering for the passed in symbol.
        /// </summary>
        /// <param name="ms">
        /// The military symbol for which to provide the installation rendering.
        /// </param>
        /// <param name="height">
        /// The height at which to provide the installation rendering.
        /// </param>
        /// <returns>
        /// The graphics Shape object that represents the installation rendering.
        /// </returns>
        private static Shape GenerateInstallation(MilSymbol ms, out double height)
        {
            ////    U&PGW   ZSGF      full unknown (flat)
            ////            AP        full air (not flat)
            ////            U         subsurface (flat)
            ////    N&L     ZSGF      full neutral (flat)
            ////            AP        full air (flat)
            ////            U         subsurface (flat)
            ////    H&S     ZSGF      full hostile (not flat)
            ////            AP        full air (not flat)
            ////            U         subsurface (flat)
            ////    F&ADMJK Z(IG)S    full friendly (not flat)
            ////            F(~IG)    full friendly (flat)
            ////            AP        full air (not flat)
            ////            U         subsurface  (flat)

            height = -185;
            Brush br = new SolidColorBrush(Colors.Black);
            int si = StandardIdentity.GetNormalizedStandardIdentity(ms.SymbolCode);

            if (SymbolData.IsTopFlat(ms.SymbolCode))
            {
                double t = ms.BaseRect.Top;
                if (si == StandardIdentity.Unknown)
                {
                    t += SymbolData.HalfWidth;
                }

                height = t - 30;
                return GenericInstallation(br, height, t);
            }

            int bd = CategoryBattleDimension.GetCode(ms.SymbolCode);
            bool ap = bd == CategoryBattleDimension.BdSpace || bd == CategoryBattleDimension.BdAir;

            // The cases cover those surfaces which are flat on top versus those that are not
            switch (si)
            {
                case StandardIdentity.Unknown:
                    // Outside for unknown space
                    if (ap)
                    {
                        return UnknownSpaceInstallation(br, height);
                    }

                    return UnknownGroundInstallation(br, height);
                case StandardIdentity.Hostile:
                    // Outside for hostile space
                    if (ap)
                    {
                        height = -165;
                        return HostileSpaceInstallation(br, height);
                    }

                    return HostileGroundInstallation(br, height);
                case StandardIdentity.Friend:
                    height = -175;
                    
                    // Outside for friendly space
                    if (ap)
                    {
                        return FriendSpaceInstallation(br, height);
                    }

                    return FriendCircleInstallation(br, height);
            }

            return null;
        }
    }
}